"""
资源规划器

实现核心的资源分配算法和优化逻辑。
"""

import logging
from typing import List, Dict, Optional, Tuple, Set
from datetime import date, timedelta
from collections import defaultdict
from dataclasses import dataclass
from enum import Enum

import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

try:
    from models.project import Project
    from models.resource import Resource
    from models.task import Task
except ImportError:
    # 如果导入失败，使用简单的数据类
    class Project:
        def __init__(self, project_id, project_name, start_date=None, end_date=None, priority=1, description=""):
            self.project_id = project_id
            self.project_name = project_name
            self.start_date = start_date
            self.end_date = end_date
            self.priority = priority
            self.description = description
            
    class Resource:
        def __init__(self, resource_id, name, department="", skills=None, availability=1.0, cost_per_hour=0.0, **kwargs):
            self.resource_id = resource_id
            self.name = name
            self.department = department
            self.skills = skills or {}
            self.availability = availability
            self.cost_per_hour = cost_per_hour
            # 支持其他参数
            for key, value in kwargs.items():
                setattr(self, key, value)
            
    class Task:
        def __init__(self, task_id, project_id, task_name, estimated_hours=0, start_date=None, end_date=None, required_skills=None, priority=1, **kwargs):
            self.task_id = task_id
            self.project_id = project_id
            self.task_name = task_name
            self.estimated_hours = estimated_hours
            self.start_date = start_date
            self.end_date = end_date
            self.required_skills = required_skills or []
            self.priority = priority
            # 支持其他参数
            for key, value in kwargs.items():
                setattr(self, key, value)


class AllocationStrategy(Enum):
    """资源分配策略"""
    SKILL_FIRST = "skill_first"      # 优先技能匹配
    WORKLOAD_FIRST = "workload_first"  # 优先工作负载平衡
    PRIORITY_FIRST = "priority_first"  # 优先任务优先级


@dataclass
class ResourceAllocation:
    """资源分配信息"""
    resource_id: str
    task_id: str
    start_date: date
    end_date: date
    allocated_hours: float
    hours_per_day: float


@dataclass
class AllocationConflict:
    """分配冲突信息"""
    resource_id: str
    date: date
    conflicting_tasks: List[str]
    total_required_hours: float
    available_hours: float
    severity: str  # "warning" or "error"


@dataclass
class PlanningResult:
    """规划结果"""
    allocations: List[ResourceAllocation]
    conflicts: List[AllocationConflict]
    unassigned_tasks: List[str]
    resource_utilization: Dict[str, float]
    warnings: List[str]
    success: bool


class ResourcePlanner:
    """资源规划器类"""

    def __init__(self, strategy: AllocationStrategy = AllocationStrategy.SKILL_FIRST):
        self.strategy = strategy
        self.logger = logging.getLogger(__name__)

    def plan_resources(self, projects: List[Project], resources: List[Resource],
                      tasks: List[Task]) -> PlanningResult:
        """
        执行资源规划

        Args:
            projects: 项目列表
            resources: 资源列表
            tasks: 任务列表

        Returns:
            规划结果
        """
        try:
            self.logger.info("开始执行资源规划...")

            # 初始化规划状态
            allocations = []
            conflicts = []
            unassigned_tasks = []
            warnings = []

            # 按策略排序任务
            sorted_tasks = self._sort_tasks_by_strategy(tasks, projects)

            # 创建资源可用性跟踪器
            resource_calendar = self._initialize_resource_calendar(resources)

            # 逐个分配任务
            for task in sorted_tasks:
                if task.is_assigned:
                    # 任务已预分配，验证分配的合理性
                    allocated_resource = next(
                        (r for r in resources if r.resource_id == task.assigned_resource_id),
                        None
                    )
                    if allocated_resource:
                        allocation_result = self._validate_existing_allocation(
                            task, allocated_resource, resource_calendar
                        )
                        if allocation_result:
                            allocations.append(allocation_result)
                        else:
                            warnings.append(f"任务 {task.task_id} 的预分配无效，重新分配")
                            task.assigned_resource_id = None

                if not task.is_assigned:
                    # 寻找合适的资源
                    best_resource = self._find_best_resource(
                        task, resources, resource_calendar
                    )

                    if best_resource:
                        allocation = self._allocate_resource_to_task(
                            task, best_resource, resource_calendar
                        )
                        if allocation:
                            allocations.append(allocation)
                            task.assign_resource(best_resource.resource_id)
                        else:
                            unassigned_tasks.append(task.task_id)
                            warnings.append(f"无法为任务 {task.task_id} 分配资源（时间冲突）")
                    else:
                        unassigned_tasks.append(task.task_id)
                        warnings.append(f"找不到合适的资源执行任务 {task.task_id}")

            # 检测冲突
            conflicts = self._detect_conflicts(allocations, resources)

            # 计算资源利用率
            utilization = self._calculate_resource_utilization(allocations, resources)

            # 判断规划是否成功
            success = len(unassigned_tasks) == 0 and len(conflicts) == 0

            result = PlanningResult(
                allocations=allocations,
                conflicts=conflicts,
                unassigned_tasks=unassigned_tasks,
                resource_utilization=utilization,
                warnings=warnings,
                success=success
            )

            self.logger.info(f"资源规划完成，分配 {len(allocations)} 个任务，"
                           f"{len(conflicts)} 个冲突，{len(unassigned_tasks)} 个未分配任务")

            return result

        except Exception as e:
            self.logger.error(f"资源规划失败：{e}")
            raise

    def _sort_tasks_by_strategy(self, tasks: List[Task],
                               projects: List[Project]) -> List[Task]:
        """根据策略排序任务"""
        project_dict = {p.project_id: p for p in projects}

        if self.strategy == AllocationStrategy.PRIORITY_FIRST:
            # 按项目优先级和任务优先级排序
            return sorted(tasks, key=lambda t: (
                -project_dict.get(t.project_id, Project('', '', date.min, date.min)).priority.value,
                -t.priority.value,
                t.start_date
            ))
        elif self.strategy == AllocationStrategy.SKILL_FIRST:
            # 按技能要求（高技能要求的任务优先）和开始时间排序
            return sorted(tasks, key=lambda t: (
                -t.required_skill_level.value,
                t.start_date,
                -t.priority.value
            ))
        else:  # WORKLOAD_FIRST
            # 按估算工时（大任务优先）和开始时间排序
            return sorted(tasks, key=lambda t: (
                t.start_date,
                -t.estimated_hours,
                -t.priority.value
            ))

    def _initialize_resource_calendar(self, resources: List[Resource]) -> Dict[str, Dict[date, float]]:
        """初始化资源日历（跟踪每日可用时间）"""
        calendar = {}

        for resource in resources:
            resource_calendar = {}
            current_date = resource.available_from

            while current_date <= resource.available_to:
                # 假设工作日为周一到周五
                if current_date.weekday() < 5:  # 0-6，0是周一
                    resource_calendar[current_date] = resource.max_hours_per_day
                else:
                    resource_calendar[current_date] = 0.0

                current_date += timedelta(days=1)

            calendar[resource.resource_id] = resource_calendar

        return calendar

    def _find_best_resource(self, task: Task, resources: List[Resource],
                           resource_calendar: Dict[str, Dict[date, float]]) -> Optional[Resource]:
        """为任务寻找最佳资源"""
        candidates = []

        for resource in resources:
            # 检查基本匹配条件
            if not resource.can_work_on_task(
                task.required_role,
                task.required_skill_level,
                task.required_skills
            ):
                continue

            # 检查时间可用性
            if not resource.is_available_on_date(task.start_date):
                continue

            # 检查是否有足够的可用时间
            available_time = self._calculate_available_time(
                resource, task.start_date, task.end_date, resource_calendar
            )

            if available_time < task.estimated_hours:
                continue

            # 计算匹配度分数
            score = self._calculate_resource_score(
                resource, task, available_time, resource_calendar
            )

            candidates.append((resource, score))

        if not candidates:
            return None

        # 选择最佳候选者
        candidates.sort(key=lambda x: x[1], reverse=True)
        return candidates[0][0]

    def _calculate_available_time(self, resource: Resource, start_date: date,
                                end_date: date, resource_calendar: Dict[str, Dict[date, float]]) -> float:
        """计算资源在指定时间段的可用时间"""
        calendar = resource_calendar.get(resource.resource_id, {})
        total_available = 0.0

        current_date = start_date
        while current_date <= end_date:
            total_available += calendar.get(current_date, 0.0)
            current_date += timedelta(days=1)

        return total_available

    def _calculate_resource_score(self, resource: Resource, task: Task,
                                available_time: float, resource_calendar: Dict[str, Dict[date, float]]) -> float:
        """计算资源与任务的匹配度分数"""
        score = 0.0

        # 基础技能匹配分数
        if resource.skill_level == task.required_skill_level:
            score += 10.0  # 完全匹配
        elif resource.skill_level > task.required_skill_level:
            score += 5.0   # 超出要求
        else:
            score -= 5.0   # 不足要求

        # 角色匹配分数
        if resource.role == task.required_role:
            score += 10.0

        # 特定技能匹配分数
        if task.required_skills:
            matched_skills = len(task.required_skills.intersection(resource.skills))
            score += matched_skills * 2.0

        # 可用时间充裕度分数
        time_buffer = available_time - task.estimated_hours
        if time_buffer > 0:
            score += min(time_buffer / task.estimated_hours * 5.0, 10.0)

        # 当前工作负载分数（优先分配给负载较轻的资源）
        current_utilization = self._calculate_current_utilization(
            resource, resource_calendar
        )
        score += (1.0 - current_utilization) * 5.0

        return score

    def _calculate_current_utilization(self, resource: Resource,
                                     resource_calendar: Dict[str, Dict[date, float]]) -> float:
        """计算资源当前利用率"""
        calendar = resource_calendar.get(resource.resource_id, {})
        total_capacity = sum(calendar.values())
        available_time = sum(calendar.values())

        if total_capacity == 0:
            return 1.0  # 无可用时间

        return 1.0 - (available_time / total_capacity)

    def _allocate_resource_to_task(self, task: Task, resource: Resource,
                                  resource_calendar: Dict[str, Dict[date, float]]) -> Optional[ResourceAllocation]:
        """将资源分配给任务"""
        calendar = resource_calendar.get(resource.resource_id, {})

        # 计算每日所需工时
        hours_per_day = task.estimated_hours / task.duration_days

        # 检查是否能够完成分配
        total_allocated = 0.0
        current_date = task.start_date

        allocation_plan = {}

        while current_date <= task.end_date and total_allocated < task.estimated_hours:
            available_today = calendar.get(current_date, 0.0)
            needed_today = min(hours_per_day, task.estimated_hours - total_allocated)
            allocated_today = min(available_today, needed_today)

            if allocated_today > 0:
                allocation_plan[current_date] = allocated_today
                total_allocated += allocated_today

            current_date += timedelta(days=1)

        # 如果无法完全分配，返回None
        if total_allocated < task.estimated_hours:
            return None

        # 更新资源日历
        for alloc_date, hours in allocation_plan.items():
            calendar[alloc_date] -= hours

        # 创建分配记录
        allocation = ResourceAllocation(
            resource_id=resource.resource_id,
            task_id=task.task_id,
            start_date=task.start_date,
            end_date=task.end_date,
            allocated_hours=total_allocated,
            hours_per_day=hours_per_day
        )

        return allocation

    def _validate_existing_allocation(self, task: Task, resource: Resource,
                                     resource_calendar: Dict[str, Dict[date, float]]) -> Optional[ResourceAllocation]:
        """验证现有分配的有效性"""
        # 检查资源是否能承担任务
        if not resource.can_work_on_task(
            task.required_role,
            task.required_skill_level,
            task.required_skills
        ):
            return None

        # 尝试分配
        return self._allocate_resource_to_task(task, resource, resource_calendar)

    def _detect_conflicts(self, allocations: List[ResourceAllocation],
                         resources: List[Resource]) -> List[AllocationConflict]:
        """检测资源分配冲突"""
        conflicts = []
        resource_dict = {r.resource_id: r for r in resources}

        # 按资源和日期分组
        daily_allocations = defaultdict(lambda: defaultdict(list))

        for allocation in allocations:
            current_date = allocation.start_date
            while current_date <= allocation.end_date:
                daily_allocations[allocation.resource_id][current_date].append(allocation)
                current_date += timedelta(days=1)

        # 检查每个资源每天的分配
        for resource_id, date_allocations in daily_allocations.items():
            resource = resource_dict.get(resource_id)
            if not resource:
                continue

            for check_date, day_allocations in date_allocations.items():
                total_hours = sum(alloc.hours_per_day for alloc in day_allocations)
                available_hours = resource.max_hours_per_day

                if total_hours > available_hours:
                    severity = "error" if total_hours > available_hours * 1.2 else "warning"

                    conflict = AllocationConflict(
                        resource_id=resource_id,
                        date=check_date,
                        conflicting_tasks=[alloc.task_id for alloc in day_allocations],
                        total_required_hours=total_hours,
                        available_hours=available_hours,
                        severity=severity
                    )
                    conflicts.append(conflict)

        return conflicts

    def _calculate_resource_utilization(self, allocations: List[ResourceAllocation],
                                       resources: List[Resource]) -> Dict[str, float]:
        """计算资源利用率"""
        utilization = {}

        for resource in resources:
            # 计算总可用时间
            total_available_days = resource.availability_days
            workdays = total_available_days * 5 / 7  # 假设每周5个工作日
            total_capacity = workdays * resource.max_hours_per_day

            # 计算已分配时间
            allocated_hours = sum(
                alloc.allocated_hours for alloc in allocations
                if alloc.resource_id == resource.resource_id
            )

            # 计算利用率
            if total_capacity > 0:
                utilization[resource.resource_id] = allocated_hours / total_capacity
            else:
                utilization[resource.resource_id] = 0.0

        return utilization